home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / stdwin / Packs / textedit / textlow.c < prev    next >
Text File  |  1995-12-21  |  14KB  |  724 lines

  1. /* Text Edit, low-level routines */
  2.  
  3. /* XXX Should make return-less functions void */
  4.  
  5. /* CONVENTION:
  6.     routines beginning with te... have a first parameter 'tp';
  7.     routines beginning with z... don't, or are macros that
  8.     implicitly use a variable or parameter 'tp'
  9. */
  10.  
  11. #include "text.h"
  12.  
  13. extern void dprintf _ARGS((char *, ...));
  14.  
  15. /* Forward */
  16. static void teinvertfocus();
  17. static void teinvert();
  18. static void teshift();
  19. static void teoffset();
  20. static void temoveup();
  21. static void temovedown();
  22.  
  23. /* Variants on wtextwidth, wtextbreak, wdrawtext taking the gap in account.
  24.    These have two buffer offsets as parameters instead of a text pointer
  25.    and a length; tetextbreak also returns a buffer offset */
  26.  
  27. /* These routines now also take tabs into account.
  28.    They should now only be called with a line start for 'pos' ! */
  29.  
  30. int
  31. tetextwidth(tp, pos, end)
  32.     register TEXTEDIT *tp;
  33.     bufpos pos, end;
  34. {
  35.     char *p= tp->buf + pos;
  36.     register char *e= tp->buf +
  37.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  38.     int w= 0;
  39.     register char *k;
  40.     
  41.     zcheckpos(pos);
  42.     zcheckpos(end);
  43.     zassert(pos <= end);
  44.     
  45.     /* This do loop is executed once or twice only! */
  46.     do {
  47.         for (k= p; k < e; ++k) {
  48.             if (*k == '\t') {
  49.                 if (k > p)
  50.                     w += wtextwidth(p, (int)(k-p));
  51.                 w= znexttab(w);
  52.                 p= k+1;
  53.             }
  54.         }
  55.         if (k > p)
  56.             w += wtextwidth(p, (int)(k-p));
  57.         if (tp->gap >= pos) {
  58.             p= tp->buf + zgapend;
  59.             e= tp->buf + end;
  60.         }
  61.     } while (k < e);
  62.     
  63.     return w;
  64. }
  65.  
  66. bufpos
  67. tetextbreak(tp, pos, end, width)
  68.     register TEXTEDIT *tp;
  69.     bufpos pos, end;
  70.     int width;
  71. {
  72.     char *p= tp->buf + pos;
  73.     register char *e= tp->buf +
  74.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  75.     int w= 0;
  76.     register char *k;
  77.     
  78.     zcheckpos(pos);
  79.     zcheckpos(end);
  80.     zassert(pos <= end);
  81.     
  82.     /* This do loop is executed once or twice only! */
  83.     do {
  84.         for (k= p; k < e; ++k) {
  85.             if (*k == '\t') {
  86.                 if (k > p) {
  87.                     int n= wtextbreak(p, (int)(k-p),
  88.                         width-w);
  89.                     if (n < k-p)
  90.                         return p - tp->buf + n;
  91.                     w += wtextwidth(p, (int)(k-p));
  92.                 }
  93.                 w= znexttab(w);
  94.                 if (w > width)
  95.                     return k - tp->buf;
  96.                 p= k+1;
  97.             }
  98.         }
  99.         if (k > p) {
  100.             int n= wtextbreak(p, (int)(k-p), width-w);
  101.             if (n < k-p)
  102.                 return p - tp->buf + n;
  103.             w += wtextwidth(p, (int)(k-p));
  104.         }
  105.         if (tp->gap >= pos) {
  106.             p= tp->buf + zgapend;
  107.             e= tp->buf + end;
  108.         }
  109.     } while (k < e);
  110.     
  111.     return end;
  112. }
  113.  
  114. hcoord
  115. tedrawtext(tp, h, v, pos, end)
  116.     register TEXTEDIT *tp;
  117.     int h, v;
  118.     bufpos pos, end;
  119. {
  120.     char *p= tp->buf + pos;
  121.     register char *e= tp->buf +
  122.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  123.     int w= 0;
  124.     register char *k;
  125.     
  126.     zcheckpos(pos);
  127.     zcheckpos(end);
  128.     zassert(pos <= end);
  129.     
  130.     /* This do loop is executed once or twice only! */
  131.     do {
  132.         for (k= p; k < e; ++k) {
  133.             if (*k == '\t') {
  134.                 if (k > p)
  135.                     wdrawtext(h+w, v, p, (int)(k-p));
  136.                     w += wtextwidth(p, (int)(k-p));
  137.                 w= znexttab(w);
  138.                 p= k+1;
  139.             }
  140.         }
  141.         if (k > p) {
  142.             wdrawtext(h+w, v, p, (int)(k-p));
  143.             w += wtextwidth(p, (int)(k-p));
  144.         }
  145.         if (tp->gap >= pos) {
  146.             p= tp->buf + zgapend;
  147.             e= tp->buf + end;
  148.         }
  149.     } while (k < e);
  150.     
  151.     return h+w;
  152. }
  153.  
  154. /* Safe memory allocation - these abort instead of returning NULL */
  155.  
  156. char *
  157. zmalloc(n)
  158.     int n;
  159. {
  160.     char *p= malloc((unsigned)n);
  161.     
  162.     if (p == NULL) {
  163.         dprintf("zmalloc(%d): out of mem", n);
  164.         exit(1);
  165.     }
  166.     return p;
  167. }
  168.  
  169. char *
  170. zrealloc(p, n)
  171.     char *p;
  172.     int n;
  173. {
  174.     char *q= realloc(p, (unsigned)n);
  175.     if (q == NULL) {
  176.         dprintf("zrealloc(0x%lx, %d): out of mem", (long)p, n);
  177.         exit(1);
  178.     }
  179.     return q;
  180. }
  181.  
  182. /* Create/destroy a text-edit record */
  183.  
  184. TEXTEDIT *
  185. tealloc(win, left, top, width)
  186.     WINDOW *win;
  187. {
  188.     return tesetup(win, left, top, left+width, top, TRUE);
  189. }
  190.  
  191. TEXTEDIT *
  192. tecreate(win, left, top, right, bottom)
  193.     WINDOW *win;
  194.     int left, top, right, bottom;
  195. {
  196.     return tesetup(win, left, top, right, bottom, TRUE);
  197. }
  198.  
  199. /*ARGSUSED*/
  200. TEXTEDIT *
  201. tesetup(win, left, top, right, bottom, drawing)
  202.     WINDOW *win;
  203.     int left, top, right, bottom;
  204.     bool drawing;
  205. {
  206.     TEXTEDIT *tp= (TEXTEDIT*) zmalloc(sizeof(TEXTEDIT));
  207.     TEXTATTR saveattr;
  208.     
  209.     tp->win= win;
  210.     tp->left= left;
  211.     tp->top= top;
  212.     tp->right= right;
  213.     tp->width= right-left;
  214.  
  215.     tp->viewing= FALSE;
  216.     
  217.     wgettextattr(&saveattr);
  218.     if (win != NULL) {
  219.         wgetwintextattr(win, &tp->attr);
  220.         wsettextattr(&tp->attr);
  221.     }
  222.     else
  223.         tp->attr = saveattr;
  224.     tp->vspace= wlineheight();
  225.     tp->tabsize= 8*wcharwidth(' ');
  226.     if (win != NULL)
  227.         wsettextattr(&saveattr);
  228.     
  229.     tp->bottom= tp->top + tp->vspace;
  230.     
  231.     tp->foc= tp->foclen= 0;
  232.     
  233.     tp->buflen= 1;
  234.     tp->buf= zmalloc(tp->buflen);
  235.     
  236.     tp->gap= 0;
  237.     tp->gaplen= tp->buflen;
  238.     
  239.     tp->nlines= 1;
  240.     tp->nstart= STARTINCR;
  241.     tp->start= (bufpos*) zmalloc(tp->nstart*sizeof(bufpos));
  242.     tp->start[0]= tp->start[1]= tp->buflen;
  243.     
  244.     tp->aim= UNDEF;
  245.     tp->focprev= FALSE;
  246.     tp->hilite= FALSE;
  247.     tp->mdown= FALSE;
  248.     tp->drawing= tp->active= drawing;
  249.     tp->opt_valid= FALSE;
  250.     
  251.     if (tp->active)
  252.         tesetcaret(tp);
  253.     
  254.     zcheck();
  255.     
  256.     return tp;
  257. }
  258.  
  259.  
  260. void
  261. tedestroy(tp)
  262.     register TEXTEDIT *tp;
  263. {
  264.     if (tp->viewing)
  265.         wchange(tp->win, tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  266.     else
  267.         wchange(tp->win, tp->left, tp->top, tp->right, tp->bottom);
  268.     tefree(tp);
  269. }
  270.  
  271. void
  272. tefree(tp)
  273.     register TEXTEDIT *tp;
  274. {
  275.     if (tp->active) {
  276.         wnocaret(tp->win);
  277.         tehidefocus(tp);
  278.     }
  279.     if (tp->buf != NULL)
  280.         free(tp->buf);
  281.     if (tp->start != NULL)
  282.         free((char*)tp->start);
  283.     free((char*)tp);
  284. }
  285.  
  286. void
  287. tesetactive(tp, active)
  288.     register TEXTEDIT *tp;
  289.     bool active;
  290. {
  291.     if (!tp->drawing || tp->active == active)
  292.         return;
  293.     tp->active = active;
  294.     if (active) {
  295.         tesetcaret(tp);
  296.     }
  297.     else {
  298.         wnocaret(tp->win);
  299.         tehidefocus(tp);
  300.     }
  301. }
  302.  
  303. /* Show/hide the focus highlighting.
  304.    The boolean hilite is set when highlighting is visible.
  305.    teshowfocus ensures the highlighting is visible (if applicable);
  306.    tehidefocus ensures it is invisible.
  307.    teinvertfocus does the hard work (it is also called from zdraw) */
  308.  
  309. teshowfocus(tp)
  310.     register TEXTEDIT *tp;
  311. {
  312.     if (tp->active && !tp->hilite && tp->foclen > 0) {
  313.         wbegindrawing(tp->win);
  314.         if (tp->viewing)
  315.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  316.         teinvertfocus(tp);
  317.         wenddrawing(tp->win);
  318.         tp->hilite= TRUE;
  319.     }
  320. }
  321.  
  322. tehidefocus(tp)
  323.     register TEXTEDIT *tp;
  324. {
  325.     if (tp->hilite) {
  326.         wbegindrawing(tp->win);
  327.         if (tp->viewing)
  328.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  329.         teinvertfocus(tp);
  330.         wenddrawing(tp->win);
  331.         tp->hilite= FALSE;
  332.     }
  333. }
  334.  
  335. static void
  336. teinvertfocus(tp)
  337.     register TEXTEDIT *tp;
  338. {
  339.     teinvert(tp, tp->foc, zfocend);
  340. }
  341.  
  342. /* Change to a new focus.
  343.    Sometimes this may keep the focus visible, sometimes not. */
  344.  
  345. techangefocus(tp, f1, f2)
  346.     register TEXTEDIT *tp;
  347.     int f1, f2;
  348. {
  349.     if (tp->hilite) {
  350.         wbegindrawing(tp->win);
  351.         if (tp->viewing)
  352.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  353.         if (f1 == tp->foc)
  354.             teinvert(tp, zfocend, f2);
  355.         else if (f2 == zfocend)
  356.             teinvert(tp, f1, tp->foc);
  357.         else {
  358.             teinvert(tp, tp->foc, zfocend);
  359.             tp->hilite= FALSE;
  360.         }
  361.         wenddrawing(tp->win);
  362.     }
  363.     tp->foc= f1;
  364.     tp->foclen= f2-f1;
  365. }
  366.  
  367. /* Low-level interface: invert the area between f1 and f2 */
  368.  
  369. static void
  370. teinvert(tp, f1, f2)
  371.     register TEXTEDIT *tp;
  372.     int f1, f2;
  373. {
  374.     coord h, v, hlast, vlast;
  375.     
  376.     if (f1 == f2)
  377.         return;
  378.     if (f2 < f1) {
  379.         int temp= f1;
  380.         f1= f2;
  381.         f2= temp;
  382.     }
  383.     
  384.     tewhichpoint(tp, f1, &h, &v);
  385.     tewhichpoint(tp, f2, &hlast, &vlast);
  386.     
  387.     if (v == vlast)
  388.         winvert(h, v, hlast, v + tp->vspace);
  389.     else {
  390.         winvert(h, v, tp->right, v + tp->vspace);
  391.         winvert(tp->left, v + tp->vspace, tp->right, vlast);
  392.         winvert(tp->left, vlast, hlast, vlast + tp->vspace);
  393.     }
  394. }
  395.  
  396. /* Draw procedure */
  397.  
  398. void
  399. tedraw(tp)
  400.     register TEXTEDIT *tp;
  401. {
  402.     tedrawnew(tp, tp->left, tp->top, tp->right, tp->bottom);
  403. }
  404.  
  405. void
  406. tedrawnew(tp, left, top, right, bottom)
  407.     register TEXTEDIT *tp;
  408.     coord left, top, right, bottom;
  409. {
  410.     lineno ifirst, ilast, i;
  411.  
  412.     /* Clip given area to view */
  413.     if (tp->viewing) {
  414.         CLIPMIN(left, tp->vleft);
  415.         CLIPMIN(top, tp->vtop);
  416.         CLIPMAX(right, tp->vright);
  417.         CLIPMAX(bottom, tp->vbottom);
  418.     }
  419.     
  420.     /* Restrict drawing to intersection of view and given area */
  421.     wcliprect(left, top, right, bottom);
  422.     
  423.     /* Compute first, last line to be drawn */
  424.     ifirst= (top - tp->top)/tp->vspace;
  425.     if (ifirst < 0)
  426.         ifirst= 0;
  427.     ilast= (bottom - tp->top + tp->vspace - 1)/tp->vspace;
  428.     if (ilast > tp->nlines)
  429.         ilast= tp->nlines;
  430.     
  431.     /* Draw lines ifirst...ilast-1 */
  432.     for (i= ifirst; i < ilast; ++i) {
  433.         bufpos pos= tp->start[i];
  434.         bufpos end= tp->start[i+1];
  435.         if (end > pos && zcharbefore(end) == EOL)
  436.             zdecr(&end);
  437.         while (end > pos && zcharbefore(end) == ' ')
  438.             zdecr(&end);
  439.         (void) tedrawtext(tp, tp->left, tp->top + i*tp->vspace,
  440.             pos, end);
  441.     }
  442.     if (tp->hilite)
  443.         teinvertfocus(tp);
  444.     
  445.     /* Reset the clip rectangle */
  446.     wnoclip();
  447. }
  448.  
  449. /* Move the gap to a new position */
  450.  
  451. temovegapto(tp, newgap)
  452.     register TEXTEDIT *tp;
  453.     bufpos newgap;
  454. {
  455.     zcheck();
  456.     zassert(0<=newgap && newgap+tp->gaplen<=tp->buflen);
  457.     
  458.     if (newgap < tp->gap)
  459.         teshift(tp, tp->gaplen, newgap, tp->gap);
  460.     else if (newgap > tp->gap)
  461.         teshift(tp, -tp->gaplen, zgapend, newgap+tp->gaplen);
  462.     tp->gap= newgap;
  463.     
  464.     zcheck();
  465. }
  466.  
  467. /* Extend the gap */
  468.  
  469. tegrowgapby(tp, add)
  470.     register TEXTEDIT *tp;
  471.     int add;
  472. {
  473.     zcheck();
  474.     zassert(add>=0);
  475.     
  476.     tp->buf= zrealloc(tp->buf, tp->buflen + add);
  477.     teshift(tp, add, zgapend, tp->buflen);
  478.     tp->gaplen += add;
  479.     if (tp->start[tp->nlines-1] == tp->buflen)
  480.         tp->start[tp->nlines-1]= tp->buflen+add;
  481.     tp->start[tp->nlines]= (tp->buflen += add);
  482.     
  483.     zcheck();
  484. }
  485.  
  486. /* Shift buf[first..last-1] n bytes (positive right, negative left) */
  487.  
  488. static void
  489. teshift(tp, n, first, last)
  490.     register TEXTEDIT *tp;
  491.     int n;
  492.     bufpos first, last;
  493. {
  494.     teoffset(tp, n, first, last);
  495.     if (n < 0)
  496.         temovedown(tp, last-first, tp->buf+first, tp->buf+first+n);
  497.     else if (n > 0)
  498.         temoveup(tp, last-first, tp->buf+first, tp->buf+first+n);
  499. }
  500.  
  501. static void
  502. teoffset(tp, n, first, last)
  503.     register TEXTEDIT *tp;
  504.     int n;
  505.     int first, last;
  506. {
  507.     int i;
  508.     
  509.     zassert(0<=first&&first<=last&&last<=tp->buflen);
  510.     
  511.     i= 0;
  512.     while (tp->start[i] < first)
  513.         ++i;
  514.     while (tp->start[i] < last) {
  515.         tp->start[i] += n;
  516.         ++i;
  517.     }
  518. }
  519.  
  520. /*ARGSUSED*/
  521. static void
  522. temoveup(tp, n, from, to)
  523.     TEXTEDIT *tp;
  524.     int n;
  525.     char *from, *to;
  526. {
  527.     zassert(from <= to);
  528.     
  529.     from += n, to += n;
  530.     while (--n >= 0)
  531.         *--to = *--from;
  532. }
  533.  
  534. /*ARGSUSED*/
  535. static void
  536. temovedown(tp, n, from, to)
  537.     TEXTEDIT *tp;
  538.     int n;
  539.     char *from, *to;
  540. {
  541.     zassert(from >= to);
  542.     
  543.     while (--n >= 0)
  544.         *to++ = *from++;
  545. }
  546.  
  547. /* Make all start entries pointing into the gap point to beyond it
  548.    TO DO: replace by a routine to delete the focus??? */
  549.  
  550. teemptygap(tp)
  551.     register TEXTEDIT *tp;
  552. {
  553.     lineno i;
  554.     
  555.     for (i= 0; tp->start[i] < tp->gap; ++i)
  556.         ;
  557.     for (; tp->start[i] < zgapend; ++i)
  558.         tp->start[i]= zgapend;
  559. }
  560.  
  561. /* Interface for wshow that clips to the viewing rectangle */
  562.  
  563. static void
  564. teshow(tp, left, top, right, bottom)
  565.     TEXTEDIT *tp;
  566.     int left, top, right, bottom;
  567. {
  568.     if (tp->viewing) {
  569.         CLIPMIN(left, tp->vleft);
  570.         CLIPMIN(top, tp->vtop);
  571.         CLIPMAX(right, tp->vright);
  572.         CLIPMAX(bottom, tp->vbottom);
  573.     }
  574.     wshow(tp->win, left, top, right, bottom);
  575. }
  576.  
  577. /* Set the caret at the new focus position,
  578.    or display the focus highlighting, if applicable.
  579.    Also call wshow() of the focus.
  580.    As a side effect, the optimization data is invalidated */
  581.  
  582. tesetcaret(tp)
  583.     register TEXTEDIT *tp;
  584. {
  585.     coord h, v, hlast, vlast;
  586.     
  587.     tp->opt_valid = FALSE;
  588.     if (!tp->active)
  589.         return;
  590.     
  591.     tewhichpoint(tp, tp->foc, &h, &v);
  592.     
  593.     if (tp->foclen == 0) {
  594.         if (!tp->viewing ||
  595.             tp->vleft <= h && h <= tp->vright &&
  596.             tp->vtop <= v && v + tp->vspace <= tp->vbottom) {
  597.             wsetcaret(tp->win, h, v);
  598.         }
  599.         else {
  600.             wnocaret(tp->win);
  601.         }
  602.         hlast= h;
  603.         vlast= v;
  604.     }
  605.     else {
  606.         tewhichpoint(tp, zfocend, &hlast, &vlast);
  607.         wnocaret(tp->win);
  608.         teshowfocus(tp);
  609.     }
  610.     teshow(tp, h, v, hlast, vlast + tp->vspace);
  611. }
  612.  
  613. /* Coordinate transformations.
  614.    The following coordinate systems exist;
  615.    a position in the text can be expressed in any of these:
  616.    
  617.        A) offset in buffer with gap removed (used for focus)
  618.     B) offset in buffer (used for start[] array)
  619.     C) (line number, offset in line taking gap into account)
  620.     D) (h, v) on screen
  621.    
  622.    Conversions exist between successive pairs:
  623.    
  624.        A -> B: pos= zaddgap(foc)
  625.     B -> A: foc= zsubgap(pos)
  626.     
  627.     B -> C: line= zwhichline(pos, prev); offset= pos-start[line]
  628.     C -> B: pos= offset + start[line]
  629.     
  630.     C -> D: v= i*vspace; h= ztextwidth(start[i], start[i]+offset)
  631.     D -> C: i= v/wlh; offset= ztextround(i, h) - start[i]
  632. */
  633.  
  634. /* Find (h, v) corresponding to focus position */
  635.  
  636. tewhichpoint(tp, f, h_ret, v_ret)
  637.     TEXTEDIT *tp;
  638.     focpos f;
  639.     coord *h_ret, *v_ret;
  640. {
  641.     bufpos pos= zaddgap(f);
  642.     lineno i= tewhichline(tp, pos, f == tp->foc && tp->focprev);
  643.     hcoord h= tetextwidth(tp, tp->start[i], pos);
  644.     
  645.     *h_ret= h + tp->left;
  646.     *v_ret= i*tp->vspace + tp->top;
  647. }
  648.  
  649. /* To which line does the given buffer position belong? */
  650.  
  651. lineno
  652. tewhichline(tp, pos, prev)
  653.     register TEXTEDIT *tp;
  654.     bufpos pos;
  655.     bool prev; /* Cf. focprev */
  656. {
  657.     lineno i;
  658.     
  659.     for (i= 0; pos > tp->start[i+1]; ++i)
  660.         ;
  661.     if (pos == tp->start[i+1] && i+1 < tp->nlines) {
  662.         ++i;
  663.         if (prev && zcharbefore(tp->start[i]) != EOL)
  664.             --i;
  665.     }
  666.     
  667.     return i;
  668. }
  669.  
  670. /* Convert point in window to buffer position,
  671.    possibly taking double-clicking into account.
  672.    If required, the line number is also returned. */
  673.  
  674. bufpos
  675. tewhereis(tp, h, v, line_return)
  676.     register TEXTEDIT *tp;
  677.     coord h, v;
  678.     int *line_return;
  679. {
  680.     bufpos pos;
  681.     lineno i;
  682.     
  683.     i= (v - tp->top)/tp->vspace;
  684.     if (i >= tp->nlines) {
  685.         i= tp->nlines;
  686.         pos= tp->buflen;
  687.     }
  688.     else if (i < 0) {
  689.         i= 0;
  690.         pos= 0;
  691.     }
  692.     else
  693.         pos= tetextround(tp, i, h);
  694.     if (line_return != NULL)
  695.         *line_return= i;
  696.     return pos;
  697. }
  698.  
  699. /* Find the buffer position nearest to the given h coordinate,
  700.    in the given line */
  701.  
  702. bufpos
  703. tetextround(tp, i, h)
  704.     register TEXTEDIT *tp;
  705.     lineno i;
  706.     hcoord h;
  707. {
  708.     bufpos pos;
  709.     bufpos end= tp->start[i+1];
  710.     
  711.     h -= tp->left;
  712.     if (end > tp->start[i] && zcharbefore(end) == EOL)
  713.         zdecr(&end);
  714.     pos= tetextbreak(tp, tp->start[i], end, h);
  715.     
  716.     if (pos < end) {
  717.         if (h - tetextwidth(tp, tp->start[i], pos) >=
  718.             tetextwidth(tp, tp->start[i], znext(pos)) - h)
  719.             zincr(&pos);
  720.     }
  721.     
  722.     return pos;
  723. }
  724.